1 module features.msvclinker;
2 public import feature;
3 import commons;
4 
5 ///Feature which gets the msvclinker
6 Feature MSVCLinker;
7 
8 version(Posix)
9 {
10     void initialize(){}
11     void start(){}
12 }
13 else version(Windows):
14 
15 
16 pragma(lib, "ole32.lib");
17 pragma(lib, "oleaut32.lib");
18 bool hasMSVCLinker(ref Terminal t, TargetVersion v, out ExistenceStatus where)
19 {
20 	import core.sys.windows.winbase;
21 	import core.sys.windows.winnt;
22 	import core.sys.windows.com;
23 	import core.sys.windows.wtypes;
24 	import std.windows.registry;
25 	immutable supportedPre2017Versions = ["14.0"];
26 	const GUID iid_SetupConfiguration = { 0x177F0C4A, 0x1CD3, 0x4DE7, [ 0xA3, 0x2C, 0x71, 0xDB, 0xBB, 0x9F, 0xA3, 0x6D ] };
27 
28 	static interface ISetupInstance : IUnknown
29 	{
30 		// static const GUID iid = uuid("B41463C3-8866-43B5-BC33-2B0676F7F42E");
31 		static const GUID iid = { 0xB41463C3, 0x8866, 0x43B5, [ 0xBC, 0x33, 0x2B, 0x06, 0x76, 0xF7, 0xF4, 0x2E ] };
32 
33 		int GetInstanceId(BSTR* pbstrInstanceId);
34 		int GetInstallDate(LPFILETIME pInstallDate);
35 		int GetInstallationName(BSTR* pbstrInstallationName);
36 		int GetInstallationPath(BSTR* pbstrInstallationPath);
37 		int GetInstallationVersion(BSTR* pbstrInstallationVersion);
38 		int GetDisplayName(LCID lcid, BSTR* pbstrDisplayName);
39 		int GetDescription(LCID lcid, BSTR* pbstrDescription);
40 		int ResolvePath(LPCOLESTR pwszRelativePath, BSTR* pbstrAbsolutePath);
41 	}
42 
43 	static interface IEnumSetupInstances : IUnknown
44 	{
45 		// static const GUID iid = uuid("6380BCFF-41D3-4B2E-8B2E-BF8A6810C848");
46 
47 		int Next(ULONG celt, ISetupInstance* rgelt, ULONG* pceltFetched);
48 		int Skip(ULONG celt);
49 		int Reset();
50 		int Clone(IEnumSetupInstances* ppenum);
51 	}
52 
53 	static interface ISetupConfiguration : IUnknown
54 	{
55 		// static const GUID iid = uuid("42843719-DB4C-46C2-8E7C-64F1816EFD5B");
56 		static const GUID iid = { 0x42843719, 0xDB4C, 0x46C2, [ 0x8E, 0x7C, 0x64, 0xF1, 0x81, 0x6E, 0xFD, 0x5B ] };
57 
58 		int EnumInstances(IEnumSetupInstances* ppEnumInstances) ;
59 		int GetInstanceForCurrentProcess(ISetupInstance* ppInstance);
60 		int GetInstanceForPath(LPCWSTR wzPath, ISetupInstance* ppInstance);
61 	}
62 
63 
64 	if("VSINSTALLDIR" in environment && !("LDC_VSDIR_FORCE" in environment))
65 	{
66 		if(!("VSCMD_ARG_TGT_ARCH" in environment))
67 			return true;
68 		string tgtArch = environment["VSCMD_ARG_TGT_ARCH"];
69 		if(tgtArch == "x64" || tgtArch == "x32")
70 			return true;
71 	}
72 	if("LDC_VSDIR" in environment && std.file.exists(environment["LDC_VSDIR"]))
73 		return true;
74 
75 	bool detectVSInstallDirViaCOM()
76 	{
77 		import core.sys.windows.windows;
78 		import core.stdc.wchar_:wcscmp;
79 
80 		CoInitialize(null); scope(exit) CoUninitialize();
81 
82 		ISetupConfiguration setup;
83     	IEnumSetupInstances instances;
84 		ISetupInstance instance;
85 		DWORD fetched;
86 
87 		HRESULT hr = CoCreateInstance(&iid_SetupConfiguration, null, CLSCTX_ALL, &ISetupConfiguration.iid, cast(void**) &setup);
88 		if(hr != S_OK || !setup) return false;
89 
90 		scope(exit) setup.Release();
91 		if(setup.EnumInstances(&instances) != S_OK)
92 			return false;
93 		scope(exit) instances.Release();
94 
95 		BSTR thisVersionString, thisInstallDir;
96 		while (instances.Next(1, &instance, &fetched) == S_OK && fetched)
97 		{
98 			if(instance.GetInstallationVersion(&thisVersionString) != S_OK || 
99 			instance.GetInstallationPath(&thisInstallDir) != S_OK)
100 				continue;
101 			return true;
102 		}
103 		return false;
104 	}
105 
106 	if(detectVSInstallDirViaCOM())
107 		return true;
108 
109 	if(Key k = windowsGetKeyWithPath("SOFTWARE", "Microsoft", "VisualStudio", "SxS", "VS7"))
110 	if(k.getValue("15.0").value_SZ)
111 		return true;
112 	
113 	foreach (ver; supportedPre2017Versions)
114 	{
115 		Key k = windowsGetKeyWithPath("SOFTWARE", "Microsoft", "VisualStudio", ver);
116 		try
117 		{
118 			if(k !is null && k.getValue("InstallDir"))
119 				return true;
120 		}
121 		catch(Exception e){return false;}
122 	}
123 	return false;
124 }
125 
126 private bool installMSLinker(ref Terminal t, ref RealTimeConsoleInput input, TargetVersion ver, Download[] content)
127 {
128 	string[] installList = 
129 	[
130 		"Microsoft.VisualStudio.Workload.VCTools",
131 		"Microsoft.VisualStudio.Component.TestTools.BuildTools",
132 		"Microsoft.VisualStudio.Component.VC.ASAN",
133 		"Microsoft.VisualStudio.Component.VC.Tools.x86.x64"
134 	];
135 
136 	import std.algorithm:reduce;
137 
138 	auto ret = t.wait(spawnShell(content[0].getOutputPath(ver)~" --wait --passive --norestart " ~installList.reduce!((str, last) => "--add "~last~" "~str))) == 0;
139 	return ret == 0;
140 }
141 
142 
143 void initialize()
144 {
145     MSVCLinker = Feature(
146         "MSVCLinker",
147         "Windows SDK for being able to compile using D programming language",
148         ExistenceChecker(null, null, toDelegate(&hasMSVCLinker)),
149         Installation([
150             Download(
151                 DownloadURL(
152                     windows: "https://aka.ms/vs/17/release/vs_BuildTools.exe"
153                 ),
154                 outputPath: "$CWD/buildtools/vc_BuildTools.exe"
155             )], toDelegate(&installMSLinker)
156         ),
157     );
158 }
159 void start()
160 {
161     
162 }